package biolabs.mcl

import biolabs.core.{Action, ProgressApprovalResult}

import java.lang.annotation.Annotation
import java.lang.reflect.{Method, Type}

object ActionInterceptor {

	/** One of ProgressApprover, ProgressHandler and ResultHandler. */
	trait InterceptorType[I <: ActionInterceptor] {
		/** The expected return type of the interceptor method */
		val returnType: Class[_]

		/** Returns the linear representation of action, as found
		  * in the corresponding annotation ({@code @ProgressApprover},
		  * {@code @ProgressHandler}, or {@code @ProgressResult}).
		  */
		def getAction(m: Method): Option[(Array[Class[_]], Array[String])]

		/** Index of Action's type parameter representing the type taken by this
		  * type of interceptor (either progress type (1) or result type (2)).
		  */
		protected val paramTypeIdx: Int

		/** Returns the type taken by this type of interceptor for the given
		  * action, that is either the action's progress type (if this is a
		  * progress approver or a progress handler) or action's result type
		  * (if this is a result handler).
		  */
		def getParameterTypeFor(action: MultiType): MultiType =
			action.getTypeArgumentsOf(classOf[Action[_, _, _]])(paramTypeIdx)

		/** Creates an action interceptor of this type. */
		def apply(method: Method, action: MultiType, paramType: MultiType): I
	}

	val ProgressApprover = new InterceptorType[ProgressApprover] {

		// the expected return type of a progress approver is ProgressApprovalResult
		override val returnType = classOf[ProgressApprovalResult]

		// progress type is Action's type parameter number 1
		override val paramTypeIdx = 1

		override def getAction(m: Method) = {
			m.getAnnotation(classOf[biolabs.core.ProgressApprover]) match {
				case null => None
				case some => Some(some.action(), some.typeParamRefs())
			}
		}

		override def apply(method: Method, action: MultiType, paramType: MultiType) =
			biolabs.mcl.ProgressApprover(method, action, paramType)
	}

	val ProgressHandler = new InterceptorType[ProgressHandler] {

		// the expected return type of a progress handler is Void
		override val returnType = Void.TYPE

		// progress type is Action's type parameter number 1
		override val paramTypeIdx = 1

		override def getAction(m: Method) = {
			m.getAnnotation(classOf[biolabs.core.ProgressHandler]) match {
				case null => None
				case some => Some(some.action(), some.typeParamRefs())
			}
		}

		override def apply(method: Method, action: MultiType, paramType: MultiType) =
			biolabs.mcl.ProgressHandler(method, action, paramType)
	}

	val ResultHandler = new InterceptorType[ResultHandler] {

		// the expected return type of a result handler is Void
		override val returnType = Void.TYPE

		// result type is Action's type parameter number 1
		override val paramTypeIdx = 2

		override def getAction(m: Method) = {
			m.getAnnotation(classOf[biolabs.core.ResultHandler]) match {
				case null => None
				case some => Some(some.action(), some.typeParamRefs())
			}
		}

		override def apply(method: Method, action: MultiType, paramType: MultiType) =
			biolabs.mcl.ResultHandler(method, action, paramType)
	}

}

class ActionInterceptor protected (
	val method: Method,
	val actionType: MultiType
)

case class ProgressApprover(
	override val method: Method,
	override val actionType: MultiType,
	val progressType: MultiType
) extends ActionInterceptor(method, actionType)

case class ProgressHandler(
	override val method: Method,
	override val actionType: MultiType,
	val progressType: MultiType
) extends ActionInterceptor(method, actionType)

case class ResultHandler(
	override val method: Method,
	override val actionType: MultiType,
	val resultType: MultiType
) extends ActionInterceptor(method, actionType)